home *** CD-ROM | disk | FTP | other *** search
- /*
- * tclUnixAZ.c --
- *
- * This file contains the top-level command procedures for
- * commands in the Tcl core that require UNIX facilities
- * such as files and process execution. Much of the code
- * in this file is based on earlier versions contributed
- * by Karl Lehenbauer, Mark Diekhans and Peter da Silva.
- *
- * Copyright 1991 Regents of the University of California
- * Permission to use, copy, modify, and distribute this
- * software and its documentation for any purpose and without
- * fee is hereby granted, provided that this copyright
- * notice appears in all copies. The University of California
- * makes no representations about the suitability of this
- * software for any purpose. It is provided "as is" without
- * express or implied warranty.
- */
-
- #ifndef lint
- static char rcsid[] = "$Header: /user6/ouster/tcl/RCS/tclUnixAZ.c,v 1.36 92/04/16 13:32:02 ouster Exp $ SPRITE (Berkeley)";
- #endif /* not lint */
-
- #include "tclInt.h"
- #include "tclUnix.h"
-
- /*
- * The variable below caches the name of the current working directory
- * in order to avoid repeated calls to getwd. The string is malloc-ed.
- * NULL means the cache needs to be refreshed.
- */
-
- static char *currentDir = NULL;
-
- /*
- * Prototypes for local procedures defined in this file:
- */
-
- static int CleanupChildren _ANSI_ARGS_((Tcl_Interp *interp,
- int numPids, int *pidPtr, int errorId));
- static char * GetFileType _ANSI_ARGS_((int mode));
- static int StoreStatData _ANSI_ARGS_((Tcl_Interp *interp,
- char *varName, struct stat *statPtr));
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_CdCmd --
- *
- * This procedure is invoked to process the "cd" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_CdCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- char *dirName;
-
- if (argc > 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " dirName\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (argc == 2) {
- dirName = argv[1];
- } else {
- dirName = "~";
- }
- dirName = Tcl_TildeSubst(interp, dirName);
- if (dirName == NULL) {
- return TCL_ERROR;
- }
- if (currentDir != NULL) {
- ckfree(currentDir);
- currentDir = NULL;
- }
- if (chdir(dirName) != 0) {
- Tcl_AppendResult(interp, "couldn't change working directory to \"",
- dirName, "\": ", Tcl_UnixError(interp), (char *) NULL);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_CloseCmd --
- *
- * This procedure is invoked to process the "close" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_CloseCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
- int result = TCL_OK;
-
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- ((Interp *) interp)->filePtrArray[fileno(filePtr->f)] = NULL;
-
- /*
- * First close the file (in the case of a process pipeline, there may
- * be two files, one for the pipe at each end of the pipeline).
- */
-
- if (filePtr->f2 != NULL) {
- if (fclose(filePtr->f2) == EOF) {
- Tcl_AppendResult(interp, "error closing \"", argv[1],
- "\": ", Tcl_UnixError(interp), "\n", (char *) NULL);
- result = TCL_ERROR;
- }
- }
- if (fclose(filePtr->f) == EOF) {
- Tcl_AppendResult(interp, "error closing \"", argv[1],
- "\": ", Tcl_UnixError(interp), "\n", (char *) NULL);
- result = TCL_ERROR;
- }
-
- /*
- * If the file was a connection to a pipeline, clean up everything
- * associated with the child processes.
- */
-
- if (filePtr->numPids > 0) {
- if (CleanupChildren(interp, filePtr->numPids, filePtr->pidPtr,
- filePtr->errorId) != TCL_OK) {
- result = TCL_ERROR;
- }
- }
-
- ckfree((char *) filePtr);
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_EofCmd --
- *
- * This procedure is invoked to process the "eof" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_EofCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
-
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (feof(filePtr->f)) {
- interp->result = "1";
- } else {
- interp->result = "0";
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_ExecCmd --
- *
- * This procedure is invoked to process the "exec" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_ExecCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- int outputId; /* File id for output pipe. -1
- * means command overrode. */
- int errorId; /* File id for temporary file
- * containing error output. */
- int *pidPtr;
- int numPids, result;
-
- /*
- * See if the command is to be run in background; if so, create
- * the command, detach it, and return.
- */
-
- if ((argv[argc-1][0] == '&') && (argv[argc-1][1] == 0)) {
- argc--;
- argv[argc] = NULL;
- numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr,
- (int *) NULL, (int *) NULL, (int *) NULL);
- if (numPids < 0) {
- return TCL_ERROR;
- }
- Tcl_DetachPids(numPids, pidPtr);
- ckfree((char *) pidPtr);
- return TCL_OK;
- }
-
- /*
- * Create the command's pipeline.
- */
-
- numPids = Tcl_CreatePipeline(interp, argc-1, argv+1, &pidPtr,
- (int *) NULL, &outputId, &errorId);
- if (numPids < 0) {
- return TCL_ERROR;
- }
-
- /*
- * Read the child's output (if any) and put it into the result.
- */
-
- result = TCL_OK;
- if (outputId != -1) {
- while (1) {
- # define BUFFER_SIZE 1000
- char buffer[BUFFER_SIZE+1];
- int count;
-
- count = read(outputId, buffer, BUFFER_SIZE);
-
- if (count == 0) {
- break;
- }
- if (count < 0) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp,
- "error reading from output pipe: ",
- Tcl_UnixError(interp), (char *) NULL);
- result = TCL_ERROR;
- break;
- }
- buffer[count] = 0;
- Tcl_AppendResult(interp, buffer, (char *) NULL);
- }
- close(outputId);
- }
-
- if (CleanupChildren(interp, numPids, pidPtr, errorId) != TCL_OK) {
- result = TCL_ERROR;
- }
- return result;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_ExitCmd --
- *
- * This procedure is invoked to process the "exit" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_ExitCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- int value;
-
- if ((argc != 1) && (argc != 2)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ?returnCode?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (argc == 1) {
- exit(0);
- }
- if (Tcl_GetInt(interp, argv[1], &value) != TCL_OK) {
- return TCL_ERROR;
- }
- exit(value);
- return TCL_OK; /* Better not ever reach this! */
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_FileCmd --
- *
- * This procedure is invoked to process the "file" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_FileCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- char *p;
- int length, statOp;
- int mode = 0; /* Initialized only to prevent
- * compiler warning message. */
- struct stat statBuf;
- char *fileName, c;
-
- if (argc < 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " option name ?arg ...?\"", (char *) NULL);
- return TCL_ERROR;
- }
- c = argv[1][0];
- length = strlen(argv[1]);
-
- /*
- * First handle operations on the file name.
- */
-
- fileName = Tcl_TildeSubst(interp, argv[2]);
- if (fileName == NULL) {
- return TCL_ERROR;
- }
- if ((c == 'd') && (strncmp(argv[1], "dirname", length) == 0)) {
- if (argc != 3) {
- argv[1] = "dirname";
- not3Args:
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " ", argv[1], " name\"", (char *) NULL);
- return TCL_ERROR;
- }
- p = strrchr(fileName, '/');
- if (p == NULL) {
- interp->result = ".";
- } else if (p == fileName) {
- interp->result = "/";
- } else {
- *p = 0;
- Tcl_SetResult(interp, fileName, TCL_VOLATILE);
- *p = '/';
- }
- return TCL_OK;
- } else if ((c == 'r') && (strncmp(argv[1], "rootname", length) == 0)
- && (length >= 2)) {
- char *lastSlash;
-
- if (argc != 3) {
- argv[1] = "rootname";
- goto not3Args;
- }
- p = strrchr(fileName, '.');
- lastSlash = strrchr(fileName, '/');
- if ((p == NULL) || ((lastSlash != NULL) && (lastSlash > p))) {
- Tcl_SetResult(interp, fileName, TCL_VOLATILE);
- } else {
- *p = 0;
- Tcl_SetResult(interp, fileName, TCL_VOLATILE);
- *p = '.';
- }
- return TCL_OK;
- } else if ((c == 'e') && (strncmp(argv[1], "extension", length) == 0)
- && (length >= 3)) {
- char *lastSlash;
-
- if (argc != 3) {
- argv[1] = "extension";
- goto not3Args;
- }
- p = strrchr(fileName, '.');
- lastSlash = strrchr(fileName, '/');
- if ((p != NULL) && ((lastSlash == NULL) || (lastSlash < p))) {
- Tcl_SetResult(interp, p, TCL_VOLATILE);
- }
- return TCL_OK;
- } else if ((c == 't') && (strncmp(argv[1], "tail", length) == 0)
- && (length >= 2)) {
- if (argc != 3) {
- argv[1] = "tail";
- goto not3Args;
- }
- p = strrchr(fileName, '/');
- if (p != NULL) {
- Tcl_SetResult(interp, p+1, TCL_VOLATILE);
- } else {
- Tcl_SetResult(interp, fileName, TCL_VOLATILE);
- }
- return TCL_OK;
- }
-
- /*
- * Next, handle operations that can be satisfied with the "access"
- * kernel call.
- */
-
- if (fileName == NULL) {
- return TCL_ERROR;
- }
- if ((c == 'r') && (strncmp(argv[1], "readable", length) == 0)
- && (length >= 5)) {
- if (argc != 3) {
- argv[1] = "readable";
- goto not3Args;
- }
- mode = R_OK;
- checkAccess:
- if (access(fileName, mode) == -1) {
- interp->result = "0";
- } else {
- interp->result = "1";
- }
- return TCL_OK;
- } else if ((c == 'w') && (strncmp(argv[1], "writable", length) == 0)) {
- if (argc != 3) {
- argv[1] = "writable";
- goto not3Args;
- }
- mode = W_OK;
- goto checkAccess;
- } else if ((c == 'e') && (strncmp(argv[1], "executable", length) == 0)
- && (length >= 3)) {
- if (argc != 3) {
- argv[1] = "executable";
- goto not3Args;
- }
- mode = X_OK;
- goto checkAccess;
- } else if ((c == 'e') && (strncmp(argv[1], "exists", length) == 0)
- && (length >= 3)) {
- if (argc != 3) {
- argv[1] = "exists";
- goto not3Args;
- }
- mode = F_OK;
- goto checkAccess;
- }
-
- /*
- * Lastly, check stuff that requires the file to be stat-ed.
- */
-
- if ((c == 'a') && (strncmp(argv[1], "atime", length) == 0)) {
- if (argc != 3) {
- argv[1] = "atime";
- goto not3Args;
- }
- if (stat(fileName, &statBuf) == -1) {
- goto badStat;
- }
- sprintf(interp->result, "%ld", statBuf.st_atime);
- return TCL_OK;
- } else if ((c == 'i') && (strncmp(argv[1], "isdirectory", length) == 0)
- && (length >= 3)) {
- if (argc != 3) {
- argv[1] = "isdirectory";
- goto not3Args;
- }
- statOp = 2;
- } else if ((c == 'i') && (strncmp(argv[1], "isfile", length) == 0)
- && (length >= 3)) {
- if (argc != 3) {
- argv[1] = "isfile";
- goto not3Args;
- }
- statOp = 1;
- } else if ((c == 'l') && (strncmp(argv[1], "lstat", length) == 0)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " lstat name varName\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (lstat(fileName, &statBuf) == -1) {
- Tcl_AppendResult(interp, "couldn't lstat \"", argv[2],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- return TCL_ERROR;
- }
- return StoreStatData(interp, argv[3], &statBuf);
- } else if ((c == 'm') && (strncmp(argv[1], "mtime", length) == 0)) {
- if (argc != 3) {
- argv[1] = "mtime";
- goto not3Args;
- }
- if (stat(fileName, &statBuf) == -1) {
- goto badStat;
- }
- sprintf(interp->result, "%ld", statBuf.st_mtime);
- return TCL_OK;
- } else if ((c == 'o') && (strncmp(argv[1], "owned", length) == 0)) {
- if (argc != 3) {
- argv[1] = "owned";
- goto not3Args;
- }
- statOp = 0;
- #ifdef S_IFLNK
- /*
- * This option is only included if symbolic links exist on this system
- * (in which case S_IFLNK should be defined).
- */
- } else if ((c == 'r') && (strncmp(argv[1], "readlink", length) == 0)
- && (length >= 5)) {
- char linkValue[MAXPATHLEN+1];
- int linkLength;
-
- if (argc != 3) {
- argv[1] = "readlink";
- goto not3Args;
- }
- linkLength = readlink(fileName, linkValue, sizeof(linkValue) - 1);
- if (linkLength == -1) {
- Tcl_AppendResult(interp, "couldn't readlink \"", argv[2],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- return TCL_ERROR;
- }
- linkValue[linkLength] = 0;
- Tcl_SetResult(interp, linkValue, TCL_VOLATILE);
- return TCL_OK;
- #endif
- } else if ((c == 's') && (strncmp(argv[1], "size", length) == 0)
- && (length >= 2)) {
- if (argc != 3) {
- argv[1] = "size";
- goto not3Args;
- }
- if (stat(fileName, &statBuf) == -1) {
- goto badStat;
- }
- sprintf(interp->result, "%ld", statBuf.st_size);
- return TCL_OK;
- } else if ((c == 's') && (strncmp(argv[1], "stat", length) == 0)
- && (length >= 2)) {
- if (argc != 4) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " stat name varName\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- if (stat(fileName, &statBuf) == -1) {
- badStat:
- Tcl_AppendResult(interp, "couldn't stat \"", argv[2],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- return TCL_ERROR;
- }
- return StoreStatData(interp, argv[3], &statBuf);
- } else if ((c == 't') && (strncmp(argv[1], "type", length) == 0)
- && (length >= 2)) {
- if (argc != 3) {
- argv[1] = "type";
- goto not3Args;
- }
- if (lstat(fileName, &statBuf) == -1) {
- goto badStat;
- }
- interp->result = GetFileType((int) statBuf.st_mode);
- return TCL_OK;
- } else {
- Tcl_AppendResult(interp, "bad option \"", argv[1],
- "\": should be atime, dirname, executable, exists, ",
- "extension, isdirectory, isfile, lstat, mtime, owned, ",
- "readable, ",
- #ifdef S_IFLNK
- "readlink, ",
- #endif
- "root, size, stat, tail, type, ",
- "or writable",
- (char *) NULL);
- return TCL_ERROR;
- }
- if (stat(fileName, &statBuf) == -1) {
- interp->result = "0";
- return TCL_OK;
- }
- switch (statOp) {
- case 0:
- mode = (geteuid() == statBuf.st_uid);
- break;
- case 1:
- mode = S_ISREG(statBuf.st_mode);
- break;
- case 2:
- mode = S_ISDIR(statBuf.st_mode);
- break;
- }
- if (mode) {
- interp->result = "1";
- } else {
- interp->result = "0";
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * StoreStatData --
- *
- * This is a utility procedure that breaks out the fields of a
- * "stat" structure and stores them in textual form into the
- * elements of an associative array.
- *
- * Results:
- * Returns a standard Tcl return value. If an error occurs then
- * a message is left in interp->result.
- *
- * Side effects:
- * Elements of the associative array given by "varName" are modified.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- StoreStatData(interp, varName, statPtr)
- Tcl_Interp *interp; /* Interpreter for error reports. */
- char *varName; /* Name of associative array variable
- * in which to store stat results. */
- struct stat *statPtr; /* Pointer to buffer containing
- * stat data to store in varName. */
- {
- char string[30];
-
- sprintf(string, "%d", statPtr->st_dev);
- if (Tcl_SetVar2(interp, varName, "dev", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%d", statPtr->st_ino);
- if (Tcl_SetVar2(interp, varName, "ino", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%d", statPtr->st_mode);
- if (Tcl_SetVar2(interp, varName, "mode", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%d", statPtr->st_nlink);
- if (Tcl_SetVar2(interp, varName, "nlink", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%d", statPtr->st_uid);
- if (Tcl_SetVar2(interp, varName, "uid", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%d", statPtr->st_gid);
- if (Tcl_SetVar2(interp, varName, "gid", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%ld", statPtr->st_size);
- if (Tcl_SetVar2(interp, varName, "size", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%ld", statPtr->st_atime);
- if (Tcl_SetVar2(interp, varName, "atime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%ld", statPtr->st_mtime);
- if (Tcl_SetVar2(interp, varName, "mtime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- sprintf(string, "%ld", statPtr->st_ctime);
- if (Tcl_SetVar2(interp, varName, "ctime", string, TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- if (Tcl_SetVar2(interp, varName, "type",
- GetFileType((int) statPtr->st_mode), TCL_LEAVE_ERR_MSG) == NULL) {
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * GetFileType --
- *
- * Given a mode word, returns a string identifying the type of a
- * file.
- *
- * Results:
- * A static text string giving the file type from mode.
- *
- * Side effects:
- * None.
- *
- *----------------------------------------------------------------------
- */
-
- static char *
- GetFileType(mode)
- int mode;
- {
- if (S_ISREG(mode)) {
- return "file";
- } else if (S_ISDIR(mode)) {
- return "directory";
- } else if (S_ISCHR(mode)) {
- return "characterSpecial";
- } else if (S_ISBLK(mode)) {
- return "blockSpecial";
- } else if (S_ISFIFO(mode)) {
- return "fifo";
- } else if (S_ISLNK(mode)) {
- return "link";
- } else if (S_ISSOCK(mode)) {
- return "socket";
- }
- return "unknown";
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_FlushCmd --
- *
- * This procedure is invoked to process the "flush" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_FlushCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
- FILE *f;
-
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!filePtr->writable) {
- Tcl_AppendResult(interp, "\"", argv[1],
- "\" wasn't opened for writing", (char *) NULL);
- return TCL_ERROR;
- }
- f = filePtr->f2;
- if (f == NULL) {
- f = filePtr->f;
- }
- if (fflush(f) == EOF) {
- Tcl_AppendResult(interp, "error flushing \"", argv[1],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- clearerr(f);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_GetsCmd --
- *
- * This procedure is invoked to process the "gets" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_GetsCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- # define BUF_SIZE 200
- char buffer[BUF_SIZE+1];
- int totalCount, done, flags;
- OpenFile *filePtr;
- register FILE *f;
-
- if ((argc != 2) && (argc != 3)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId ?varName?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!filePtr->readable) {
- Tcl_AppendResult(interp, "\"", argv[1],
- "\" wasn't opened for reading", (char *) NULL);
- return TCL_ERROR;
- }
-
- /*
- * We can't predict how large a line will be, so read it in
- * pieces, appending to the current result or to a variable.
- */
-
- totalCount = 0;
- done = 0;
- flags = 0;
- f = filePtr->f;
- while (!done) {
- register int c, count;
- register char *p;
-
- for (p = buffer, count = 0; count < BUF_SIZE-1; count++, p++) {
- c = getc(f);
- if (c == EOF) {
- if (ferror(filePtr->f)) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "error reading \"", argv[1],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- clearerr(filePtr->f);
- return TCL_ERROR;
- } else if (feof(filePtr->f)) {
- if ((totalCount == 0) && (count == 0)) {
- totalCount = -1;
- }
- done = 1;
- break;
- }
- }
- if (c == '\n') {
- done = 1;
- break;
- }
- *p = c;
- }
- *p = 0;
- if (argc == 2) {
- Tcl_AppendResult(interp, buffer, (char *) NULL);
- } else {
- if (Tcl_SetVar(interp, argv[2], buffer, flags|TCL_LEAVE_ERR_MSG)
- == NULL) {
- return TCL_ERROR;
- }
- flags = TCL_APPEND_VALUE;
- }
- totalCount += count;
- }
-
- if (argc == 3) {
- sprintf(interp->result, "%d", totalCount);
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_OpenCmd --
- *
- * This procedure is invoked to process the "open" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_OpenCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- Interp *iPtr = (Interp *) interp;
- int pipeline, fd;
- char *access;
- register OpenFile *filePtr;
-
- if (argc == 2) {
- access = "r";
- } else if (argc == 3) {
- access = argv[2];
- } else {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " filename ?access?\"", (char *) NULL);
- return TCL_ERROR;
- }
-
- filePtr = (OpenFile *) ckalloc(sizeof(OpenFile));
- filePtr->f = NULL;
- filePtr->f2 = NULL;
- filePtr->readable = 0;
- filePtr->writable = 0;
- filePtr->numPids = 0;
- filePtr->pidPtr = NULL;
- filePtr->errorId = -1;
-
- /*
- * Verify the requested form of access.
- */
-
- pipeline = 0;
- if (argv[1][0] == '|') {
- pipeline = 1;
- }
- switch (access[0]) {
- case 'r':
- filePtr->readable = 1;
- break;
- case 'w':
- filePtr->writable = 1;
- break;
- case 'a':
- filePtr->writable = 1;
- break;
- default:
- badAccess:
- Tcl_AppendResult(interp, "illegal access mode \"", access,
- "\"", (char *) NULL);
- goto error;
- }
- if (access[1] == '+') {
- filePtr->readable = filePtr->writable = 1;
- if (access[2] != 0) {
- goto badAccess;
- }
- } else if (access[1] != 0) {
- goto badAccess;
- }
-
- /*
- * Open the file or create a process pipeline.
- */
-
- if (!pipeline) {
- char *fileName = argv[1];
-
- if (fileName[0] == '~') {
- fileName = Tcl_TildeSubst(interp, fileName);
- if (fileName == NULL) {
- goto error;
- }
- }
- filePtr->f = fopen(fileName, access);
- if (filePtr->f == NULL) {
- Tcl_AppendResult(interp, "couldn't open \"", argv[1],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- goto error;
- }
- } else {
- int *inPipePtr, *outPipePtr;
- int cmdArgc, inPipe, outPipe;
- char **cmdArgv;
-
- if (Tcl_SplitList(interp, argv[1]+1, &cmdArgc, &cmdArgv) != TCL_OK) {
- goto error;
- }
- inPipePtr = (filePtr->writable) ? &inPipe : NULL;
- outPipePtr = (filePtr->readable) ? &outPipe : NULL;
- inPipe = outPipe = -1;
- filePtr->numPids = Tcl_CreatePipeline(interp, cmdArgc, cmdArgv,
- &filePtr->pidPtr, inPipePtr, outPipePtr, &filePtr->errorId);
- ckfree((char *) cmdArgv);
- if (filePtr->numPids < 0) {
- goto error;
- }
- if (filePtr->readable) {
- if (outPipe == -1) {
- if (inPipe != -1) {
- close(inPipe);
- }
- Tcl_AppendResult(interp, "can't read output from command:",
- " standard output was redirected", (char *) NULL);
- goto error;
- }
- filePtr->f = fdopen(outPipe, "r");
- }
- if (filePtr->writable) {
- if (inPipe == -1) {
- Tcl_AppendResult(interp, "can't write input to command:",
- " standard input was redirected", (char *) NULL);
- goto error;
- }
- if (filePtr->f != NULL) {
- filePtr->f2 = fdopen(inPipe, "w");
- } else {
- filePtr->f = fdopen(inPipe, "w");
- }
- }
- }
-
- /*
- * Enter this new OpenFile structure in the table for the
- * interpreter. May have to expand the table to do this.
- */
-
- fd = fileno(filePtr->f);
- TclMakeFileTable(iPtr, fd);
- if (iPtr->filePtrArray[fd] != NULL) {
- panic("Tcl_OpenCmd found file already open");
- }
- iPtr->filePtrArray[fd] = filePtr;
- sprintf(interp->result, "file%d", fd);
- return TCL_OK;
-
- error:
- if (filePtr->f != NULL) {
- fclose(filePtr->f);
- }
- if (filePtr->f2 != NULL) {
- fclose(filePtr->f2);
- }
- if (filePtr->numPids > 0) {
- Tcl_DetachPids(filePtr->numPids, filePtr->pidPtr);
- ckfree((char *) filePtr->pidPtr);
- }
- if (filePtr->errorId != -1) {
- close(filePtr->errorId);
- }
- ckfree((char *) filePtr);
- return TCL_ERROR;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_PwdCmd --
- *
- * This procedure is invoked to process the "pwd" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_PwdCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- char buffer[MAXPATHLEN+1];
-
- if (argc != 1) {
- Tcl_AppendResult(interp, "wrong # args: should be \"",
- argv[0], "\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (currentDir == NULL) {
- #if TCL_GETWD
- if (getwd(buffer) == NULL) {
- Tcl_AppendResult(interp, "error getting working directory name: ",
- buffer, (char *) NULL);
- return TCL_ERROR;
- }
- #else
- if (getcwd(buffer, MAXPATHLEN) == NULL) {
- if (errno == ERANGE) {
- interp->result = "working directory name is too long";
- } else {
- Tcl_AppendResult(interp,
- "error getting working directory name: ",
- Tcl_UnixError(interp), (char *) NULL);
- }
- return TCL_ERROR;
- }
- #endif
- currentDir = (char *) ckalloc((unsigned) (strlen(buffer) + 1));
- strcpy(currentDir, buffer);
- }
- interp->result = currentDir;
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_PutsCmd --
- *
- * This procedure is invoked to process the "puts" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_PutsCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
- FILE *f;
-
- if (argc == 4) {
- if (strncmp(argv[3], "nonewline", strlen(argv[3])) != 0) {
- Tcl_AppendResult(interp, "bad argument \"", argv[3],
- "\": should be \"nonewline\"", (char *) NULL);
- return TCL_ERROR;
- }
- } else if (argc != 3) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId string ?nonewline?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!filePtr->writable) {
- Tcl_AppendResult(interp, "\"", argv[1],
- "\" wasn't opened for writing", (char *) NULL);
- return TCL_ERROR;
- }
-
- f = filePtr->f2;
- if (f == NULL) {
- f = filePtr->f;
- }
- fputs(argv[2], f);
- if (argc == 3) {
- fputc('\n', f);
- }
- if (ferror(f)) {
- Tcl_AppendResult(interp, "error writing \"", argv[1],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- clearerr(f);
- return TCL_ERROR;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_ReadCmd --
- *
- * This procedure is invoked to process the "read" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_ReadCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
- int bytesLeft, bytesRead, count;
- #define READ_BUF_SIZE 4096
- char buffer[READ_BUF_SIZE+1];
- int newline;
-
- if ((argc != 2) && (argc != 3)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId ?numBytes|nonewline?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (!filePtr->readable) {
- Tcl_AppendResult(interp, "\"", argv[1],
- "\" wasn't opened for reading", (char *) NULL);
- return TCL_ERROR;
- }
-
- /*
- * Compute how many bytes to read, and see whether the final
- * newline should be dropped.
- */
-
- newline = 1;
- if ((argc > 2) && isdigit(argv[2][0])) {
- if (Tcl_GetInt(interp, argv[2], &bytesLeft) != TCL_OK) {
- return TCL_ERROR;
- }
- } else {
- bytesLeft = 1<<30;
- if (argc > 2) {
- if (strncmp(argv[2], "nonewline", strlen(argv[2])) == 0) {
- newline = 0;
- } else {
- Tcl_AppendResult(interp, "bad argument \"", argv[2],
- "\": should be \"nonewline\"", (char *) NULL);
- return TCL_ERROR;
- }
- }
- }
-
- /*
- * Read the file in one or more chunks.
- */
-
- bytesRead = 0;
- while (bytesLeft > 0) {
- count = READ_BUF_SIZE;
- if (bytesLeft < READ_BUF_SIZE) {
- count = bytesLeft;
- }
- count = fread(buffer, 1, count, filePtr->f);
- if (ferror(filePtr->f)) {
- Tcl_ResetResult(interp);
- Tcl_AppendResult(interp, "error reading \"", argv[1],
- "\": ", Tcl_UnixError(interp), (char *) NULL);
- clearerr(filePtr->f);
- return TCL_ERROR;
- }
- if (count == 0) {
- break;
- }
- buffer[count] = 0;
- Tcl_AppendResult(interp, buffer, (char ) NULL);
- bytesLeft -= count;
- bytesRead += count;
- }
- if ((newline == 0) && (interp->result[bytesRead-1] == '\n')) {
- interp->result[bytesRead-1] = 0;
- }
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_SeekCmd --
- *
- * This procedure is invoked to process the "seek" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_SeekCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
- int offset, mode;
-
- if ((argc != 3) && (argc != 4)) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId offset ?origin?\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- if (Tcl_GetInt(interp, argv[2], &offset) != TCL_OK) {
- return TCL_ERROR;
- }
- mode = SEEK_SET;
- if (argc == 4) {
- int length;
- char c;
-
- length = strlen(argv[3]);
- c = argv[3][0];
- if ((c == 's') && (strncmp(argv[3], "start", length) == 0)) {
- mode = SEEK_SET;
- } else if ((c == 'c') && (strncmp(argv[3], "current", length) == 0)) {
- mode = SEEK_CUR;
- } else if ((c == 'e') && (strncmp(argv[3], "end", length) == 0)) {
- mode = SEEK_END;
- } else {
- Tcl_AppendResult(interp, "bad origin \"", argv[3],
- "\": should be start, current, or end", (char *) NULL);
- return TCL_ERROR;
- }
- }
- if (fseek(filePtr->f, offset, mode) == -1) {
- Tcl_AppendResult(interp, "error during seek: ",
- Tcl_UnixError(interp), (char *) NULL);
- clearerr(filePtr->f);
- return TCL_ERROR;
- }
-
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_SourceCmd --
- *
- * This procedure is invoked to process the "source" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_SourceCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileName\"", (char *) NULL);
- return TCL_ERROR;
- }
- return Tcl_EvalFile(interp, argv[1]);
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_TellCmd --
- *
- * This procedure is invoked to process the "tell" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_TellCmd(notUsed, interp, argc, argv)
- ClientData notUsed; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- OpenFile *filePtr;
-
- if (argc != 2) {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " fileId\"", (char *) NULL);
- return TCL_ERROR;
- }
- if (TclGetOpenFile(interp, argv[1], &filePtr) != TCL_OK) {
- return TCL_ERROR;
- }
- sprintf(interp->result, "%d", ftell(filePtr->f));
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * Tcl_TimeCmd --
- *
- * This procedure is invoked to process the "time" Tcl command.
- * See the user documentation for details on what it does.
- *
- * Results:
- * A standard Tcl result.
- *
- * Side effects:
- * See the user documentation.
- *
- *----------------------------------------------------------------------
- */
-
- /* ARGSUSED */
- int
- Tcl_TimeCmd(dummy, interp, argc, argv)
- ClientData dummy; /* Not used. */
- Tcl_Interp *interp; /* Current interpreter. */
- int argc; /* Number of arguments. */
- char **argv; /* Argument strings. */
- {
- int count, i, result;
- double timePer;
- #if TCL_GETTOD
- struct timeval start, stop;
- struct timezone tz;
- int micros;
- #else
- struct tms dummy2;
- long start, stop;
- #endif
-
- if (argc == 2) {
- count = 1;
- } else if (argc == 3) {
- if (Tcl_GetInt(interp, argv[2], &count) != TCL_OK) {
- return TCL_ERROR;
- }
- } else {
- Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
- " command ?count?\"", (char *) NULL);
- return TCL_ERROR;
- }
- #if TCL_GETTOD
- gettimeofday(&start, &tz);
- #else
- start = times(&dummy2);
- #endif
- for (i = count ; i > 0; i--) {
- result = Tcl_Eval(interp, argv[1], 0, (char **) NULL);
- if (result != TCL_OK) {
- if (result == TCL_ERROR) {
- char msg[60];
- sprintf(msg, "\n (\"time\" body line %d)",
- interp->errorLine);
- Tcl_AddErrorInfo(interp, msg);
- }
- return result;
- }
- }
- #if TCL_GETTOD
- gettimeofday(&stop, &tz);
- micros = (stop.tv_sec - start.tv_sec)*1000000
- + (stop.tv_usec - start.tv_usec);
- timePer = micros;
- #else
- stop = times(&dummy2);
- timePer = (((double) (stop - start))*1000000.0)/CLK_TCK;
- #endif
- Tcl_ResetResult(interp);
- sprintf(interp->result, "%.0f microseconds per iteration", timePer/count);
- return TCL_OK;
- }
-
- /*
- *----------------------------------------------------------------------
- *
- * CleanupChildren --
- *
- * This is a utility procedure used to wait for child processes
- * to exit, record information about abnormal exits, and then
- * collect any stderr output generated by them.
- *
- * Results:
- * The return value is a standard Tcl result. If anything at
- * weird happened with the child processes, TCL_ERROR is returned
- * and a message is left in interp->result.
- *
- * Side effects:
- * If the last character of interp->result is a newline, then it
- * is removed. File errorId gets closed, and pidPtr is freed
- * back to the storage allocator.
- *
- *----------------------------------------------------------------------
- */
-
- static int
- CleanupChildren(interp, numPids, pidPtr, errorId)
- Tcl_Interp *interp; /* Used for error messages. */
- int numPids; /* Number of entries in pidPtr array. */
- int *pidPtr; /* Array of process ids of children. */
- int errorId; /* File descriptor index for file containing
- * stderr output from pipeline. -1 means
- * there isn't any stderr output. */
- {
- int result = TCL_OK;
- int i, pid, length;
- WAIT_STATUS_TYPE waitStatus;
-
- for (i = 0; i < numPids; i++) {
- pid = Tcl_WaitPids(1, &pidPtr[i], (int *) &waitStatus);
- if (pid == -1) {
- Tcl_AppendResult(interp, "error waiting for process to exit: ",
- Tcl_UnixError(interp), (char *) NULL);
- continue;
- }
-
- /*
- * Create error messages for unusual process exits. An
- * extra newline gets appended to each error message, but
- * it gets removed below (in the same fashion that an
- * extra newline in the command's output is removed).
- */
-
- if (!WIFEXITED(waitStatus) || (WEXITSTATUS(waitStatus) != 0)) {
- char msg1[20], msg2[20];
-
- result = TCL_ERROR;
- sprintf(msg1, "%d", pid);
- if (WIFEXITED(waitStatus)) {
- sprintf(msg2, "%d", WEXITSTATUS(waitStatus));
- Tcl_SetErrorCode(interp, "CHILDSTATUS", msg1, msg2,
- (char *) NULL);
- } else if (WIFSIGNALED(waitStatus)) {
- char *p;
-
- p = Tcl_SignalMsg((int) (WTERMSIG(waitStatus)));
- Tcl_SetErrorCode(interp, "CHILDKILLED", msg1,
- Tcl_SignalId((int) (WTERMSIG(waitStatus))), p,
- (char *) NULL);
- Tcl_AppendResult(interp, "child killed: ", p, "\n",
- (char *) NULL);
- } else if (WIFSTOPPED(waitStatus)) {
- char *p;
-
- p = Tcl_SignalMsg((int) (WSTOPSIG(waitStatus)));
- Tcl_SetErrorCode(interp, "CHILDSUSP", msg1,
- Tcl_SignalId((int) (WSTOPSIG(waitStatus))), p, (char *) NULL);
- Tcl_AppendResult(interp, "child suspended: ", p, "\n",
- (char *) NULL);
- } else {
- Tcl_AppendResult(interp,
- "child wait status didn't make sense\n",
- (char *) NULL);
- }
- }
- }
- ckfree((char *) pidPtr);
-
- /*
- * Read the standard error file. If there's anything there,
- * then return an error and add the file's contents to the result
- * string.
- */
-
- if (errorId >= 0) {
- while (1) {
- # define BUFFER_SIZE 1000
- char buffer[BUFFER_SIZE+1];
- int count;
-
- count = read(errorId, buffer, BUFFER_SIZE);
-
- if (count == 0) {
- break;
- }
- if (count < 0) {
- Tcl_AppendResult(interp,
- "error reading stderr output file: ",
- Tcl_UnixError(interp), (char *) NULL);
- break;
- }
- buffer[count] = 0;
- Tcl_AppendResult(interp, buffer, (char *) NULL);
- }
- close(errorId);
- }
-
- /*
- * If the last character of interp->result is a newline, then remove
- * the newline character (the newline would just confuse things).
- */
-
- length = strlen(interp->result);
- if ((length > 0) && (interp->result[length-1] == '\n')) {
- interp->result[length-1] = '\0';
- }
-
- return result;
- }
-